<div id="content">
	<h1>Active Record</h1>
	<p class="mar_t_10">
	　　虽然 dpDbCommand 可以处理几乎任何数据库相关的任务，但很可能我们会花费 90% 的时间以编写一些执行普通 CRUD（create, read, update 和 delete）操作的 SQL 语句。 而且我们的代码中混杂了SQL语句时也会变得难以维护。要解决这些问题，我们可以使用 Active Record。<br />

	　　Active Record (AR) 是一个流行的 对象-关系映射 (ORM) 技术。 每个 AR 类代表一个数据表（或视图），数据表（或视图）的列在 AR 类中体现为类的属性，一个 AR 实例则表示表中的一行。 常见的 CRUD 操作作为 AR 的方法实现。因此，我们可以以一种更加面向对象的方式访问数据。 例如，我们可以使用以下代码向 <strong>tbl_post</strong> 表中插入一个新行。
	</p>
	<pre class="syntax php">&lt;?php
	$post=new Post;
	$post->title='标题';
	$post->content='一段内容...';
	$post->save();
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　下面我们讲解怎样设置 AR 并通过它执行 CRUD 操作。我们将在下一节中展示怎样使用 AR 处理数据库关系。 为简单起见，我们使用下面的数据表作为此节中的例子。注意，如果你使用的不是 <strong>MySQL</strong> 数据库，你应该将下面的 SQL 中的 <strong>AUTO_INCREMENT</strong> 替换为 <strong>AUTOINCREMENT</strong>。
	</p>
	<pre class="syntax php">
CREATE TABLE tbl_post (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    title VARCHAR(128) NOT NULL,
    content TEXT NOT NULL,
    create_time INTEGER NOT NULL
);</pre>
	<br />
	<div class="notice">注意: AR 并非要解决所有数据库相关的任务。它的最佳应用是模型化数据表为 PHP 结构和执行不包含复杂 SQL 语句的查询。对于复杂查询的场景，应使用 dpDbCommand。</div>
	<br />
	<p class="mar_t_10">　　对 AR 的支持受 DBMS 的限制，当前只支持下列几种 DBMS：
		<ul class="list">
			<li>　　　●　MySQL 4.1 或更高版本</li>
			<li>　　　●　SQLite 2 和 3</li>
			<li>　　　●　Microsoft SQL Server 2000 或更高版本</li>
		</ul>
	</p>
	<div class="code">
		小帮助：由于 Active Record 依靠表的元数据（metadata）测定列的信息，读取元数据并解析需要时间。 如果你数据库的表结构很少改动，你应该通过配置文件对 schema_caching 属性的值为一个大于零的值开启表结构缓存。
	</div>
	<br />
	<h1>定义 AR 类 (建立数据模型)</h1>
	<p class="mar_t_10">
	　　要访问一个数据表，我们首先需要通过继承 <strong>dpActiveRecord</strong> 定义一个 AR 类。每个 AR 类代表一个单独的数据表，一个 AR 实例则代表那个表中的一行。 如下例子演示了代表 tbl_post 表的 AR 类的最简代码： </p>
	<pre class="syntax php">&lt;?php
	class Post extends dpActiveRecord
	{
	    public static function model($className=__CLASS__)
	    {
	        return parent::model($className);
	    }
	 
	    public function tableName()
	    {
	        return 'tbl_post';	//如果表前缀已设置，这里可写成 {post}
	    }
	}
?&gt;</pre>
	<br />
	<div class="code">
		提示: 由于 AR 类经常在多处被引用，我们可以导入包含 AR 类的整个目录，而不是一个个导入。 例如，如果我们所有的 AR 类文件都在 app/model 目录中，我们可以配置应用如下：
		<pre class="syntax php">&lt;?php
		return array(
			'import'=>array(
		    	'app:model.*',
			),
		);
?&gt;</pre>
	</div>
	<br />
	<p class="mar_t_10">
	　　如果表中有 <strong>主键</strong> ，则必须在相应的 AR 类中通过下面的方式，覆盖父类的一个方法，为 AR 类指明主键，下面是例子：
	</p>
	<pre class="syntax php">&lt;?php
	public function primaryKey()
	{
	    return 'id';
	}
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　如果需要连接的 <strong>从库</strong> ，需要在相应的 AR 类中覆盖父类的一个方法，指明连接的从库名，下面是例子：
	</p>
	<pre class="syntax php">&lt;?php
	public function linkName()
	{
	    return 'slave_db';
	}
?&gt;</pre>
	<br />
	<h1>创建记录</h1>
	<p class="mar_t_10">
	　　要向数据表中插入新行，我们要创建一个相应 AR 类的实例，设置其与表的列相关的属性，然后调用 <strong>insert()</strong> 方法完成插入：
	</p>
	<pre class="syntax php">&lt;?php
	$post=new Post;
	$post->title='演示标题';
	$post->content='演示内容。。。';
	$post->create_time=time();
	$post->insert();
?&gt;</pre>
	<br />
	<div class="notice">
		注意：不要试图给一个不存在的字段赋值，比如 $post->age=16 ，表结构中并没有 age 这个字段，如果调用不存在的字段属性，AR 将会抛出一个异常。
	</div>
	<p class="mar_t_10">
	　　如果表的主键是自增的，在插入完成后，AR 实例将包含一个更新的主键。在上面的例子中， id 属性将反映出新插入帖子的主键值，即使我们从未显式地改变它。<br />

	　　如果一个字段在表结构中使用了静态默认值（例如一个字符串，一个数字）定义。则 AR 实例中相应的属性将在此实例创建时自动含有此默认值。改变此默认值的一个方式就是在 AR 类中显示定义此属性：
	</p>
	<pre class="syntax php">&lt;?php
	class Post extends dpActiveRecord
	{
	    public $title='默认标题';
	    ......
	}
	 
	$post=new Post;
	echo $post->title;  // 这儿将显示: 默认标题
?&gt;</pre>
	<br />
	<h1>读取记录</h1>
	<p class="mar_t_10">
	　　要读取数据表中的数据，我们可以通过如下方式调用 find 系列方法中的一种：
	</p>
	<pre class="syntax php">&lt;?php
	// 查找满足指定条件的结果中的第一行
	$post=Post::model()->find($condition,$params);
	// 查找具有指定主键值的那一行
	$post=Post::model()->findByPk($postID,$condition,$params);
	// 查找具有指定属性值的行
	$post=Post::model()->findByAttribute($attributes,$condition,$params);
	// 通过指定的 SQL 语句查找结果中的第一行
	$post=Post::model()->findBySql($sql,$params);
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　如上所示，我们通过 <strong>Post::model()</strong> 调用 <strong>find</strong> 方法。 请记住，静态方法 <strong>model()</strong> 是每个 AR 类都拥有的。 此方法返回在对象上下文中的一个用于访问类级别方法（类似于静态类方法的东西）的 AR 实例。<br />

	　　如果 find 方法找到了一个满足查询条件的行，它将返回一个 <strong>Post</strong> 实例，实例的属性含有数据表行中相应列的值。 然后我们就可以像读取普通对象的属性那样读取载入的值，例如 <strong>echo $post->title;</strong>。<br />

	　　如果使用给定的查询条件在数据库中没有找到任何东西， <strong>find</strong> 方法将返回 <strong>null</strong> 。<br />

	　　调用 <strong>find</strong> 时，我们使用 <strong>$condition</strong> 和 <strong>$params</strong> 指定查询条件。此处 <strong>$condition</strong> 可以是 SQL 语句中的 WHERE 字符串，<strong>$params</strong> 则是一个参数数组，其中的值应绑定到 </strong>$condation</strong> 中的占位符。例如：
	</p>
	<pre class="syntax php">&lt;?php
	// 查找 postID=10 的那一行
	$post=Post::model()->find('postID=:postID', array(':postID'=>10));
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　当有多行数据匹配指定的查询条件时，我们可以通过下面的 <strong>finds</strong> 方法将他们全部带回。 每个都有其各自的 find 方法，就像我们已经讲过的那样。
	</p>
	<pre class="syntax php">&lt;?php
	// 查找满足指定条件的所有行
	$posts=Post::model()->finds($condition,$params);
	// 查找带有指定主键的所有行
	$posts=Post::model()->findsByPk($postIDs,$condition,$params);
	// 查找带有指定属性值的所有行
	$posts=Post::model()->findsByAttribute($attributes,$condition,$params);
	// 通过指定的SQL语句查找所有行
	$posts=Post::model()->findsBySql($sql,$params);
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　如果没有任何东西符合查询条件，<strong>finds</strong> 将返回一个 <strong>空数组</strong>。这跟 find 不同，find 会在没有找到什么东西时返回 null。<br />

	　　除了上面讲述的 find 和 finds 方法，为了方便，DP还提供了统计行数的方法：
	</p>
	<pre class="syntax php">&lt;?php
	// 获取满足指定条件的行数
	$rows=Post::model()->count();
	// 通过指定的 SQL 获取结果行数
	$rows=Post::model()->countBySql($sql,$params);
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　执行完 <strong>find()</strong> 或者 <strong>finds()</strong> 类操作后，AR 会保存上次查询到的行数记录，所以可直接通过 <strong>count()</strong> 方法直接获取查询的行数。
	</p>
	<h1>更新记录</h1>
	<p class="mar_t_10">
	　　在 AR 实例填充了列的值之后，我们可以改变它们并把它们存回数据表。
	</p>
	<pre class="syntax php">&lt;?php
	$post->title='新标题';
	$post->updateByPk(10); // 将更改保存到数据库
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　DP 的 AR 提供了共两种更新方法，如下所示：
	</p>
	<pre class="syntax php">&lt;?php
	// 更新符合指定条件的行
	Post::model()->updates($attributes,$condition,$params);
	// 更新符合指定条件和主键的行
	Post::model()->updateByPk($pk,$attributes,$condition,$params);
?&gt;</pre>
	<br />
	<h1>删除记录</h1>
	<p class="mar_t_10">
	　　如果一个 AR 实例被一行数据填充,我们也可以删除此行数据。<br />
	　　DP 提供了共两种删除方法，如下所示：
	</p>
	<pre class="syntax php">&lt;?php
	// 删除符合指定条件的行
	Post::model()->deletes($condition,$params);
	// 删除符合指定条件和主键的行
	Post::model()->deleteByPk($pk,$condition,$params);
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　下面的例子是操作 AR 类如何删除一行记录的。<br />
	</p>
	<pre class="syntax php">&lt;?php
	$post->deleteByPk(10); // 从数据表中删除此行
?&gt;</pre>
	<br />
	<h1>自定义</h1>
	<p class="mar_t_10">
	　　dpActiveRecord 提供了几个占位符方法，可以在子类中覆盖父类方法，实现自定义其工作流。
		<ul class="list">
			<li>　　　●　<strong>beforeSave</strong> 和 <strong>afterSave</strong>: 这两个将在添加或者更新 AR 实例之前和之后被调用。</li>
			<li>　　　●　<strong>beforeDelete</strong> 和 <strong>afterDelete</strong>: 这两个将在一个 AR 实例被删除之前和之后被调用。</li>
			<li>　　　●　<strong>beforeFind</strong> 和 <strong>afterFind</strong>: 这两个将在一个 AR 实例被查询之前和之后被调用。</li>
		</ul>
	</p>
	<br />
	<h1>命名范围(查询范围)</h1>
	<p class="mar_t_10">
	　　命名范围(named scope) 表示一个 命名的（named） 查询规则，它可以和其他命名范围联合使用并应用于 Active Record 查询。<br />

	　　命名范围主要是在 <strong>dpActiveRecord::scopes()</strong> 方法中以名字-规则对的方式声明。 如下代码在 Post 模型类中声明了两个命名范围, <strong>published</strong> 和 <strong>recently</strong>。
	</p>
	<pre class="syntax php">&lt;?php
	class Post extends dpActiveRecord
	{
	    ......
	    public function scopes()
	    {
	        return array(
	            'published'=>array(
	                'condition'=>'status=1',
	            ),
	            'recently'=>array(
	                'order'=>'create_time DESC',
	                'limit'=>5,
	            ),
	        );
	    }
	}
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　每个命名范围声明为一个数组。例如，<strong>recently</strong> 命名范围指定 order 属性为 create_time DESC ， <strong>limit</strong> 属性为 5。他们翻译为查询规则后就会返回最近的5篇帖子。<br />
	　　命名范围多用作 find 方法调用的修改器。 几个命名范围可以链到一起形成一个更有约束性的查询结果集。例如， 要找到最近发布的帖子， 我们可以使用如下代码：
	</p>
	<pre class="syntax php">&lt;?php
	$posts=Post::model()->published()->recently()->finds();
?&gt;</pre>
	<br />
	<h1>参数化的命名范围</h1>
	<p class="mar_t_10">
	　　命名范围可以参数化。例如， 我们想自定义 recently 命名范围中指定的帖子数量，要实现此目的，我们需要使用一个占位符 <strong>{数字}</strong> 这个数字从0开始，下面的例子来展示如何定义参数化命名范围：
	</p>
	<pre class="syntax php">&lt;?php
	class Post extends dpActiveRecord
	{
	    ......
	    public function scopes()
	    {
	        return array(
	            'recently'=>array(
	                'order'=>'create_time {0}',	//这里定义一个占位符{0}
	                'limit'=>{1},				//这里定义一个占位符{1}
	            ),
	        );
	    }
	}
?&gt;</pre>
	<br />
	<p class="mar_t_10">
	　　我们就可以使用如下语句获取3条并且倒序的帖子。
	</p>
	<pre class="syntax php">&lt;?php
	$posts=Post::model()->recently('DESC', 3)->finds();
?&gt;</pre>
</div>
<script type="text/javascript">
$(function() {
	jQuery.syntax();
});
</script>